package control;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import server.EchoServer;
import server.mysqlConnection;

/**
 * @author Limor
  *This class responsible for the operation reports, when the client wants to activate this operation 
  *he chooses a year for the report in the GUI client. The year inserted into string array that will send to the server, 
  * In the server we maps the message by checking the first string (see AbstractControl).
 */
public class ReportsControl extends AbstractControl {
	private Double[] sumDiffrencePop;
	private Double[] sumDiffrenceReg;
	private Double[] sumDiffrenceLate;
	private Float averagePop ;
	private Long medianPop ;
	private Float averageReg ;
	private Long medianReg ;
	private Float averageLate ;
	private Long medianLate ;
	private int flag=0 ; 
	private HashMap<String,Double> calcForEachBook;
	private HashMap<String,Double> numDelays ;
	private String[] bookNames ; 
	private String[] info;
	

	public Object anlyze(String[] msg) {
		ArrayList<Object> result=new ArrayList<Object>();
		result.add("Reports");
		if(msg[1].contentEquals("Activity"))
		{
			/// ACTIVITY REPORT
			activityRepors();
			result.add("Activity");
			result.add(info);
			return result ; 
		}
		
		
		else if(msg[1].contentEquals("Borrowed"))
		{
			result.add("Borrowed");
			
			/// BORROWED BOOKS REPORT
			int year=Integer.parseInt(msg[2]);
			calcDiffrence(1,year);
			String[] ans=new String[4];
			if(flag==1)
			{
				ans[0]=averagePop.toString();
				ans[1]=medianPop.toString() ;
				flag=0;
			}
			else { ans[0]="NO RESULT"; ans[1]="NO RESULT"; }
			
			calcDiffrence(0,year);
			if(flag==1)
			{
				ans[2]=averageReg.toString();
				ans[3]=medianReg.toString() ;
				flag=0;
			}
			
			else { ans[2]="NO RESULT"; ans[3]="NO RESULT"; }
			
			result.add(ans);
			
			result.add(sumDiffrencePop);
			result.add(sumDiffrenceReg);
			
			return result ; 
		}
		
		else{
			
		/// LATES REPORT
			int year=Integer.parseInt(msg[1]);
			calcLates(year);
			
			String[] lateAns=new String[2];
			if(flag==1)
			{
				lateAns[0]=averageLate.toString();
				lateAns[1]=medianLate.toString() ;
			}
			else { lateAns[0]="NO RESULT"; lateAns[1]="NO RESULT"; }
			result.add(lateAns);
			
			result.add(calcForEachBook);
			result.add(numDelays);
			result.add(bookNames);
			return result;	
		}
	}

	
	
	/**
	 * This function calculate the fields for the first Reports
	 */
	public void activityRepors()
	{
		info=new String[5];
		String notActive=numberOFNonActive();
		String active=numberOfActive();
		String frozen=numberOfFrozen();
		String borrowed=numberOfBorrowedBooks();
		String lates=numberOfLates();
		info[0]=active;
		info[1]=notActive;
		info[2]=frozen;
		info[3]=borrowed;
		info[4]=lates;
	}
	
	
	
	/**
	 * @return the number of lates
	 */
	public String numberOfLates() {
		return mysqlConnection.check("SELECT COUNT(sid)  FROM borrow WHERE incident='late';");
	}



	/**
	 * @return the number of borrowed books
	 */
	public String numberOfBorrowedBooks() {
		return mysqlConnection.check("SELECT COUNT(bid)  FROM books WHERE isborrowed=1;");
	}



	/**
	 * @return the number of frozen subscriptions
	 */
	public String numberOfFrozen() {
		return mysqlConnection.check("SELECT COUNT(sid)  FROM subscriber WHERE CardStatus=2;");
	}



	/**
	 * @return the number of active subscriptions
	 */
	public String numberOfActive() {
		return mysqlConnection.check("SELECT COUNT(sid)  FROM subscriber WHERE CardStatus=1;");
	}



	/**
	 * @return the number of non active subscriptions
	 */
	public String numberOFNonActive() {
		return mysqlConnection.check("SELECT COUNT(sid)  FROM subscriber WHERE CardStatus=0;");
	}



	/**
	 *  This function calculates duration of borrow in days for each month for the selected year.
	 * @param isPopular 1-poplar books, 0-regular books. 
	 * @param selectedYear the year that chosen for the report.
	 */
	public void calcDiffrence(int isPopular,int selectedYear){
		String[][] books=mysqlConnection.select("SELECT borrow.bId, borrowDate, returnDate FROM borrow,books WHERE borrow.bId=books.bid AND borrow.copy=books.copy AND returnDate<>'00/00/00' AND ispopular="+isPopular+";",3);
		ArrayList<Long> diff=new ArrayList<Long>(); 
		ArrayList<Long> diffForMedian=new ArrayList<Long>();
		
		int cnt=0 ; 
		
			for(int i=0; i<books.length; i++)
			{
				try 
				{
					DateFormat formatter=new SimpleDateFormat("yyyy/MM/dd");
					String dateBorrow=books[i][1];
					Date borrowD = (Date)formatter.parse(dateBorrow);
					Calendar borrowDate=Calendar.getInstance();
					borrowDate.setTime(borrowD);
						
					int year=borrowDate.get(Calendar.YEAR);
					if(year==selectedYear)
					{
						cnt++;   // number of match books
						
						String dateReturn=books[i][2];
						
						long x=calcToDiff(borrowDate, dateReturn);
					     diff.add(x);	
					     diffForMedian.add(x);
					     
						int month = borrowDate.get(Calendar.MONTH);
	
						if(isPopular!=0) sumDiffrencePop[month+1]+=diff.get(i);
						else sumDiffrenceReg[month+1]+=diff.get(i);
					}	
					else  diff.add((long) 0);	
				} 
				catch (ParseException e)  {e.printStackTrace(); }

			}
			

			if(isPopular==1){
				averagePop=calcAvg(1,cnt);
				if(diffForMedian.size()!=0) medianPop=(Long) calcMedian(diffForMedian);			
			}
		
			else{
				averageReg=calcAvg(0,cnt);
				if(diffForMedian.size()!=0) medianReg=(Long) calcMedian(diffForMedian);	
			}
	}
	
	
	
	
	/**
	 * This function calculates the number of times and duration of lates in days for each book for the selected year. 
	 * @param selectedYear - the year that chosen for the report.
	 */
	public void calcLates(int selectedYear){
		String[][] books=mysqlConnection.select("SELECT borrow.bId, borrow.expectedReturnDate, returnDate, books.name FROM borrow,books WHERE books.bid=borrow.bid AND borrow.copy=books.copy AND incident='Late' AND returnDate<>'00/00/00';",4); 
		ArrayList<Long> diff=new ArrayList<Long>();  
		ArrayList<Long> diffForMedian=new ArrayList<Long>();
	
		int cnt=0;
		
			for(int i=0 ; i<books.length; i++)
			{
				String expectedReturnDate=books[i][1];
				String dateReturn=books[i][2];
				String name=books[i][3];
				try
				{
					DateFormat formatter=new SimpleDateFormat("yyyy/MM/dd");
					Date expctDate = (Date)formatter.parse(expectedReturnDate);
					Calendar expectedReturn=Calendar.getInstance();
					expectedReturn.setTime(expctDate);
					
					int year=expectedReturn.get(Calendar.YEAR);
					if(year==selectedYear)
					{
						cnt++;
						
						long x=calcToDiff(expectedReturn, dateReturn);
					    diff.add(x);	
					    diffForMedian.add(x);
					    
						int month = expectedReturn.get(Calendar.MONTH);
	
						sumDiffrenceLate[month+1]+=diff.get(i);
						
						calcForEachBook.put(name, calcForEachBook.get(name)+x);   // calc duration 
						if(x!=0) numDelays.put(name, numDelays.get(name)+1);				// calc number 
						
					}
					else  diff.add((long) 0);	
				
				}
				catch (ParseException e)  {e.printStackTrace(); }
			}
			
			averageLate=calcAvg(2,cnt);
			if(diffForMedian.size()!=0) medianLate=(Long) calcMedian(diffForMedian);		

		}
	
	
	
	/**
	 * This function calculates the difference in days between two dates.
	 * @param date1 - Calendar of the first date
	 * @param date2 - String of the second date
	 * @return 0- if the difference if negative else return the difference.
	 */
	public long calcToDiff(Calendar date1, String date2)
	{
		try
		{
			DateFormat formatter=new SimpleDateFormat("yyyy/MM/dd");
			Date returnD = (Date)formatter.parse(date2);
			Calendar returnDate=Calendar.getInstance();
			returnDate.setTime(returnD);
				
			 long milis1 = date1.getTimeInMillis();
		     long milis2 = returnDate.getTimeInMillis();
			
		     if(milis2<milis1) return 0;   // check again 
		     long x=(milis2-milis1)/(24 * 60 * 60 * 1000);  
		     return x;
		}
		catch (ParseException e)  {e.printStackTrace(); return 0; }
	}
	
	
	

	/**
	 * This function find median.
	 * @param values array 
	 * @return the median of the array 
	 */
	public long calcMedian(ArrayList<Long> values)
	{
	    Collections.sort(values);
	    
	    return (long) values.get((values.size()+1)/2-1);
	}
	
	
	
	
	/**
	 * This function calculates the average.
	 * @param whichArray - 1 - popular array, 0 - regular array, 2- late array 
	 * @param len Array length
	 * @return the average.
	 */
	public float calcAvg(int whichArray, int len)
	{
		int sum=0;
		
		if(len!=0){
			flag=1;
			
			if(whichArray==1)
				for(int k=0; k<13; k++)
					sum+=sumDiffrencePop[k];
			else if(whichArray==0)
				for(int k=0; k<13; k++)
					sum+=sumDiffrenceReg[k];
			else 
				for(int k=0; k<13; k++)
					sum+=sumDiffrenceLate[k];
				
			
			float ans=(float) ((float)sum/len);
			return ans;
			
		}
		else return 0;	
	}
	
	

	/**
	 * Constructor - initialize the arrays in class 
	 */
	public ReportsControl()
	{
			if(EchoServer.bookOrdersQueue!=null)
			{
				int numBooks=EchoServer.bookOrdersQueue.size();
				sumDiffrencePop=new Double[13];
				sumDiffrenceReg=new Double[13];
				sumDiffrenceLate=new Double[13];
				calcForEachBook=new HashMap<String,Double>();  
				numDelays=new HashMap<String,Double>(); 
				bookNames=new String[numBooks];
				
				for(int i=0; i<13;i++)
				{
					sumDiffrencePop[i]=0.0;
					sumDiffrenceReg[i]=0.0;
					sumDiffrenceLate[i]=0.0;
				}
				
				
				String booksInfo[][]=mysqlConnection.select("SELECT DISTINCT bid,name FROM books;",2);   
			
				
				for(int i=0; i<booksInfo.length; i++)
				{
					String bName=booksInfo[i][1];
					calcForEachBook.put(bName, 0.0);
					numDelays.put(bName,  0.0);
					bookNames[i]=booksInfo[i][1];
				}
			}
			
	}
	
	
	
	@Override
	public Boolean checkExist() {
		return null;
		// TODO Auto-generated method stub
		
	}

}
